home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / pyshared / ufw / common.py < prev    next >
Encoding:
Python Source  |  2009-04-03  |  12.0 KB  |  377 lines

  1. #
  2. # common.py: common classes for ufw
  3. #
  4. # Copyright (C) 2008-2009 Canonical Ltd.
  5. #
  6. #    This program is free software: you can redistribute it and/or modify
  7. #    it under the terms of the GNU General Public License version 3,
  8. #    as published by the Free Software Foundation.
  9. #
  10. #    This program is distributed in the hope that it will be useful,
  11. #    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. #    GNU General Public License for more details.
  14. #
  15. #    You should have received a copy of the GNU General Public License
  16. #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17. #
  18.  
  19. import re
  20. import socket
  21. import ufw.util
  22. from ufw.util import debug
  23.  
  24. programName = "ufw"
  25. state_dir = "/var/lib/ufw"
  26. config_dir = "/etc"
  27. prefix_dir = "/usr"
  28.  
  29. class UFWError(Exception):
  30.     '''This class represents ufw exceptions'''
  31.     def __init__(self, value):
  32.         self.value = value
  33.  
  34.     def __str__(self):
  35.         return repr(self.value)
  36.  
  37.  
  38. class UFWRule:
  39.     '''This class represents firewall rules'''
  40.     def __init__(self, action, protocol, dport="any", dst="0.0.0.0/0",
  41.                  sport="any", src="0.0.0.0/0"):
  42.         # Be sure to update dup_rule accordingly...
  43.         self.remove = False
  44.         self.updated = False
  45.         self.v6 = False
  46.         self.dst = ""
  47.         self.src = ""
  48.         self.dport = ""
  49.         self.sport = ""
  50.         self.protocol = ""
  51.         self.multi = False
  52.         self.dapp = ""
  53.         self.sapp = ""
  54.         self.action = ""
  55.         self.position = 0
  56.         self.logtype = ""
  57.         try:
  58.             self.set_action(action)
  59.             self.set_protocol(protocol)
  60.             self.set_port(dport)
  61.             self.set_port(sport, "src")
  62.             self.set_src(src)
  63.             self.set_dst(dst)
  64.         except UFWError:
  65.             raise
  66.  
  67.     def __str__(self):
  68.         return self.format_rule()
  69.  
  70.     def dup_rule(self):
  71.         '''Return a duplicate of a rule'''
  72.         rule = UFWRule(self.action, self.protocol)
  73.         rule.remove = self.remove
  74.         rule.updated = self.updated
  75.         rule.v6 = self.v6
  76.         rule.dst = self.dst
  77.         rule.src = self.src
  78.         rule.dport = self.dport
  79.         rule.sport = self.sport
  80.         rule.multi = self.multi
  81.         rule.dapp = self.dapp
  82.         rule.sapp = self.sapp
  83.         rule.position = self.position
  84.         rule.logtype = self.logtype
  85.  
  86.         return rule
  87.  
  88.     def format_rule(self):
  89.         '''Format rule for for later parsing'''
  90.         str = ""
  91.  
  92.         # Protocol is handled below
  93.         if self.protocol == "any":
  94.             str = " -p all"
  95.         else:
  96.             str = " -p " + self.protocol
  97.  
  98.             if self.multi:
  99.                 str += " -m multiport"
  100.                 if self.dport != "any" and self.sport != "any":
  101.                     str += " --dports " + self.dport
  102.                     str += " -m multiport"
  103.                     str += " --sports " + self.sport
  104.                 elif self.dport != "any":
  105.                     str += " --dports " + self.dport
  106.                 elif self.sport != "any":
  107.                     str += " --sports " + self.sport
  108.  
  109.         if self.dst != "0.0.0.0/0" and self.dst != "::/0":
  110.             str += " -d " + self.dst
  111.         if not self.multi and self.dport != "any":
  112.             str += " --dport " + self.dport
  113.         if self.src != "0.0.0.0/0" and self.src != "::/0":
  114.             str += " -s " + self.src
  115.         if not self.multi and self.sport != "any":
  116.             str += " --sport " + self.sport
  117.  
  118.         lstr = ""
  119.         if self.logtype != "":
  120.             lstr = "_" + self.logtype
  121.         if self.action == "allow":
  122.             str += " -j ACCEPT%s" % (lstr)
  123.         elif self.action == "reject":
  124.             str += " -j REJECT%s" % (lstr)
  125.             if self.protocol == "tcp":
  126.                 # follow TCP's default and send RST
  127.                 str += " --reject-with tcp-reset"
  128.         elif self.action == "limit":
  129.             # Caller needs to change this
  130.             str += " -j LIMIT%s" % (lstr)
  131.         else:
  132.             str += " -j DROP%s" % (lstr)
  133.  
  134.         if self.dapp != "" or self.sapp != "":
  135.             # Format the comment string, and quote it just in case
  136.             comment = "-m comment --comment '"
  137.             pat_space = re.compile(' ')
  138.             if self.dapp != "":
  139.                 comment += "dapp_" + pat_space.sub('%20', self.dapp)
  140.             if self.dapp != "" and self.sapp != "":
  141.                 comment += ","
  142.             if self.sapp != "":
  143.                 comment += "sapp_" + pat_space.sub('%20', self.sapp)
  144.             comment += "'"
  145.  
  146.             str += " " + comment
  147.  
  148.         return str.strip()
  149.  
  150.     def set_action(self, action):
  151.         '''Sets action of the rule'''
  152.         tmp = action.lower().split('_')
  153.         if tmp[0] == "allow" or tmp[0] == "reject" or tmp[0] == "limit":
  154.             self.action = tmp[0]
  155.         else:
  156.             self.action = "deny"
  157.  
  158.         logtype = ""
  159.         if len(tmp) > 1:
  160.              logtype = tmp[1]
  161.         self.set_logtype(logtype)
  162.  
  163.     def set_port(self, port, loc="dst"):
  164.         '''Sets port and location (destination or source) of the rule'''
  165.         err_msg = _("Bad port '%s'") % (port)
  166.         if port == "any":
  167.             pass
  168.         elif loc == "dst" and self.dapp:
  169.             pass
  170.         elif loc == "src" and self.sapp:
  171.             pass
  172.         elif re.match(r'^[,:]', port) or re.match(r'[,:]$', port):
  173.             raise UFWError(err_msg)
  174.         elif (port.count(',') + port.count(':')) > 14:
  175.             # Limitation of iptables
  176.             raise UFWError(err_msg)
  177.         else:
  178.             ports = port.split(',')
  179.             if len(ports) < 1:
  180.                 raise UFWError(err_msg)
  181.             elif len(ports) > 1:
  182.                 self.multi = True
  183.  
  184.             tmp = ""
  185.             for p in ports:
  186.                 if re.match(r'^\d+:\d+$', p):
  187.                     # Port range
  188.                     self.multi = True
  189.                     ran = p.split(':')
  190.                     if len(ran) != 2:
  191.                         raise UFWError(err_msg)
  192.                     for q in ran:
  193.                         if int(q) < 1 or int(q) > 65535:
  194.                             raise UFWError(err_msg)
  195.                     if int(ran[0]) >= int(ran[1]):
  196.                         raise UFWError(err_msg)
  197.                 elif re.match('^\d+$', p):
  198.                     if int(p) < 1 or int(p) > 65535:
  199.                         raise UFWError(err_msg)
  200.                 elif re.match(r'^\w[\w\-]+', p):
  201.                     try:
  202.                         p = socket.getservbyname(p)
  203.                     except Exception, (error):
  204.                         raise UFWError(err_msg)
  205.                 else:
  206.                     raise UFWError(err_msg)
  207.  
  208.                 if tmp:
  209.                     tmp += "," + str(p)
  210.                 else:
  211.                     tmp = str(p)
  212.  
  213.             port = tmp
  214.  
  215.         if loc == "src":
  216.             self.sport = str(port)
  217.         else:
  218.             self.dport = str(port)
  219.  
  220.     def set_protocol(self, protocol):
  221.         '''Sets protocol of the rule'''
  222.         if protocol == "tcp" or protocol == "udp" or protocol == "any":
  223.             self.protocol = protocol
  224.         else:
  225.             err_msg = _("Unsupported protocol '%s'") % (protocol)
  226.             raise UFWError(err_msg)
  227.  
  228.     def _fix_anywhere(self):
  229.         '''Adjusts src and dst based on v6'''
  230.         if self.v6:
  231.             if self.dst and (self.dst == "any" or self.dst == "0.0.0.0/0"):
  232.                 self.dst = "::/0"
  233.             if self.src and (self.src == "any" or self.src == "0.0.0.0/0"):
  234.                 self.src = "::/0"
  235.         else:
  236.             if self.dst and (self.dst == "any" or self.dst == "::/0"):
  237.                 self.dst = "0.0.0.0/0"
  238.             if self.src and (self.src == "any" or self.src == "::/0"):
  239.                 self.src = "0.0.0.0/0"
  240.  
  241.     def set_v6(self, v6):
  242.         '''Sets whether this is ipv6 rule, and adjusts src and dst 
  243.            accordingly.
  244.         '''
  245.         self.v6 = v6
  246.         self._fix_anywhere()
  247.  
  248.     def set_src(self, addr):
  249.         '''Sets source address of rule'''
  250.         tmp = addr.lower()
  251.  
  252.         if tmp != "any" and not ufw.util.valid_address(tmp, "any"):
  253.             err_msg = _("Bad source address")
  254.             raise UFWError(err_msg)
  255.         self.src = tmp
  256.         self._fix_anywhere()
  257.  
  258.     def set_dst(self, addr):
  259.         '''Sets destination address of rule'''
  260.         tmp = addr.lower()
  261.  
  262.         if tmp != "any" and not ufw.util.valid_address(tmp, "any"):
  263.             err_msg = _("Bad destination address")
  264.             raise UFWError(err_msg)
  265.         self.dst = tmp
  266.         self._fix_anywhere()
  267.  
  268.     def set_position(self, num):
  269.         '''Sets the position of the rule'''
  270.         if not re.match(r'^[0-9]+', str(num)):
  271.             err_msg = _("Insert position '%s' is not a valid position") % (num)
  272.             raise UFWError(err_msg)
  273.         self.position = int(num)
  274.  
  275.     def set_logtype(self, logtype):
  276.         '''Sets logtype of the rule'''
  277.         if logtype.lower() == "log" or logtype.lower() == "log-all" or \
  278.            logtype == "":
  279.             self.logtype = logtype.lower()
  280.         else:
  281.             err_msg = _("Invalid log type '%s'") % (logtype)
  282.             raise UFWError(err_msg)
  283.  
  284.     def normalize(self):
  285.         '''Normalize src and dst to standard form'''
  286.         changed = False
  287.         if self.src:
  288.             try:
  289.                 (self.src, changed) = ufw.util.normalize_address(self.src, \
  290.                                                                  self.v6)
  291.             except Exception:
  292.                 raise
  293.                 err_msg = _("Could not normalize source address")
  294.                 raise UFWError(err_msg)
  295.         if changed:
  296.             self.updated = changed
  297.  
  298.         if self.dst:
  299.             try:
  300.                 (self.dst, changed) = ufw.util.normalize_address(self.dst, \
  301.                                                                    self.v6)
  302.             except Exception:
  303.                 err_msg = _("Could not normalize destination address")
  304.                 raise UFWError(err_msg)
  305.  
  306.         if self.dport:
  307.             ports = self.dport.split(',')
  308.             ufw.util.human_sort(ports)
  309.             self.dport = ','.join(ports)
  310.  
  311.         if self.sport:
  312.             ports = self.sport.split(',')
  313.             ufw.util.human_sort(ports)
  314.             self.sport = ','.join(ports)
  315.  
  316.         if changed:
  317.             self.updated = changed
  318.  
  319.     def match(x, y):
  320.         '''Check if rules match
  321.         Return codes:
  322.           0  match
  323.           1  no match
  324.          -1  match all but action
  325.         '''
  326.         if not x or not y:
  327.             raise ValueError()
  328.  
  329.         dbg_msg = _("No match")
  330.         if x.dport != y.dport:
  331.             debug(dbg_msg)
  332.             return 1
  333.         if x.sport != y.sport:
  334.             debug(dbg_msg)
  335.             return 1
  336.         if x.protocol != y.protocol:
  337.             debug(dbg_msg)
  338.             return 1
  339.         if x.src != y.src:
  340.             debug(dbg_msg)
  341.             return 1
  342.         if x.dst != y.dst:
  343.             debug(dbg_msg)
  344.             return 1
  345.         if x.v6 != y.v6:
  346.             debug(dbg_msg)
  347.             return 1
  348.         if x.dapp != y.dapp:
  349.             debug(dbg_msg)
  350.             return 1
  351.         if x.sapp != y.sapp:
  352.             debug(dbg_msg)
  353.             return 1
  354.         if x.action == y.action and x.logtype == y.logtype:
  355.             dbg_msg = _("Found exact match")
  356.             debug(dbg_msg)
  357.             return 0
  358.         dbg_msg = _("Found non-action/non-logtype match (%s/%s %s/%s)") % \
  359.                     (x.action, y.action, x.logtype, y.logtype)
  360.         debug(dbg_msg)
  361.         return -1
  362.  
  363.     def get_app_tuple(self):
  364.         '''Returns a tuple to identify an app rule'''
  365.         tuple = ""
  366.         if self.dapp != "" or self.sapp != "":
  367.             tuple = "%s %s %s %s" % (self.dapp, self.dst, self.sapp, self.src)
  368.             if self.dapp == "":
  369.                 tuple = "%s %s %s %s" % (self.dport, self.dst, self.sapp, \
  370.                                          self.src)
  371.             if self.sapp == "":
  372.                 tuple = "%s %s %s %s" % (self.dapp, self.dst, self.sport, \
  373.                                          self.src)
  374.  
  375.         return tuple
  376.  
  377.